package judger

import (
	"bytes"
	"fmt"
	"gugojudger/utils/ojchannel"
	"os/exec"
	"strconv"
	"strings"
	"syscall"
	"time"
)

//开始运行并trace跟踪
func runAndCollectTrace(cmd *exec.Cmd, X *ojchannel.OJChannelX) {
	startTime = time.Now().UnixNano()
	traceChan := make(chan *strace.TraceRecord)
	done := make(chan error, 1)

	go func() {
		done <- strace.Trace(cmd, strace.RecordTraces(traceChan))
		close(traceChan)
	}()
	var maxMem int64
	for r := range traceChan {
		if r.Syscall != nil {
			//fmt.Printf("%d\t %d\n", r.Syscall.Regs.Orig_rax, r.Syscall.Sysno)
			analyseMemory(cmd, &maxMem)
			analyseSyscall(cmd, r.Syscall.Sysno, maxMem, X)
		}
	}

	if err := <-done; err != nil {
		X.C <- ojchannel.Status(ojchannel.MNT_ERR, "Trace exited with error:"+err.Error())
	}
}

func analyseSyscall(cmd *exec.Cmd, cID int, memUse int64, X *ojchannel.OJChannelX) {
	switch cID {
	case syscall.SYS_FORK, syscall.SYS_VFORK: //fork
		{
			X.C <- ojchannel.Status(ojchannel.MNT_FATAL, "")
			cmd.Process.Signal(syscall.SIGINT)
			return
		}
	case syscall.SYS_EXIT_GROUP: //exit_group
		{
			endTime = time.Now().UnixNano()
			X.C <- ojchannel.Status(ojchannel.MNT_INF, stdoutBuf.String())
			X.C <- ojchannel.Status(ojchannel.MNT_TIME, formatRunTime(endTime, startTime))
			X.C <- ojchannel.Status(ojchannel.MNT_EOF, memUse)
			return
		}
	}
}

func analyseMemory(cmd *exec.Cmd, max *int64) {
	mem := getStatm(cmd.Process.Pid)
	if mem > *max {
		*max = mem
	}
}
func getStatm(pid int) int64 {
	cmd := exec.Command("cat", fmt.Sprintf("%s%d%s", "/proc/", pid, "/statm"))
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Run()
	strs := strings.Fields(out.String())
	if len(strs) > 2 {
		x, err := strconv.ParseInt(strs[1], 10, 64)
		if err != nil {
			return -1
		}
		return x / 4
	}
	return -1
}

func gotMT(v interface{}) (mem int64, cpuTime int64) {
	var param syscall.Rusage
	timeB, _ := json.Marshal(v)
	err := json.Unmarshal(timeB, &param)
	if err != nil {
		fmt.Println(err.Error())
		return -1, -1
	}
	//Unix use Maxrss
	return param.Maxrss, param.Utime.Usec

}
